/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.exec;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import combi.sipper.io.InFlatFile;

public class ExtractRegulons {

	/**
	 * Regulon = set of genes that interacts in the regulation of a TU.
	 * - Each regulon is composed of Proteins (or proteins complexes) and regulates a set of Regulation Units (RUs).
	 * 		when a gene produce the protein, it exists in the regulon, but not always... 
	 * - A regulation unit is a set of many things and contains some TF biding sites (TFBSs) which regulate Promoter
	 * - Promoter with TFBS regulate Transcription Unit (TU) that contains genes.
	 * @throws IOException 
	 */

	private static String GENE = "GENE";
	private static String REGULATES = "REGULATES";
	private static String UNMODIFIED_FORM = "UNMODIFIED-FORM";
	private static String REGULATED_ENTITY = "REGULATED-ENTITY";
	private static String REGULATOR = "REGULATOR";
	private static String REGULATOR_MODE = "MODE";
	private static String COMPONENT_OF = "COMPONENT-OF";
	private static String COMPONENTS = "COMPONENTS";


	public static void main(String[] args) throws IOException
	{
		String fileRegulons = args[0];
		String fileRegulation = args[1];
		String fileOut = args[2];
		String fileTU = args[3];
		String filePromoters = args[4];
		String fileGenes = args[5];

		InFlatFile ioRegulation = new InFlatFile(fileRegulation);
		Map<String,Map<String, Collection<String>>> regulationMap = ioRegulation.readFile();
		InFlatFile ioTU = new InFlatFile(fileTU);
		Map<String,Map<String, Collection<String>>> tuMap = ioTU.readFile();
		InFlatFile ioPromoters = new InFlatFile(filePromoters);
		Map<String,Map<String, Collection<String>>> promoterMap = ioPromoters.readFile();
		InFlatFile ioGenes = new InFlatFile(fileGenes);
		Map<String,Map<String, Collection<String>>> geneMap = ioGenes.readFile();

		Map<String, Collection<String>> regulationGenes = new TreeMap<String, Collection<String>>();
		Map<String, Collection<String>> regulationTUs = new TreeMap<String, Collection<String>>();
		Map<String, String> regulationMode = new TreeMap<String, String>();

		InFlatFile regIO = new InFlatFile(fileRegulons);
		Map<String, Map<String,Collection<String>>> regulonMap = regIO.readFile();
		System.err.println(regulonMap.size() + " regulons in input");

		for (Entry<String, Map<String, Collection<String>>> entry : regulonMap.entrySet())
		{
			Collection<String> entities = new TreeSet<String>();
			Collection<String> regulatedGenes = new TreeSet<String>();
			Collection<String> regulatedTUs = new TreeSet<String>();
			if (entry.getValue().containsKey(REGULATES))
			{
				entities.addAll(entry.getValue().get(REGULATES));
			}
			for (String elem : entities)
			{
				if (regulationMap.containsKey(elem))
				{
					Map<String,Collection<String>> regul = regulationMap.get(elem);
					//get the regulated entities
					if (regul.containsKey(REGULATED_ENTITY))
					{
						for (String regulated: regul.get(REGULATED_ENTITY))
						{
							// get the promoters of regulated by the regulation entity
							if(promoterMap.containsKey(regulated))
							{
								Map<String, Collection<String>> pro = promoterMap.get(regulated);
								//get TU in which the promoter is used
								if (pro.containsKey(COMPONENT_OF))
								{
									Collection<String> componentsTU = pro.get(COMPONENT_OF);
									regulatedTUs.addAll(componentsTU); //potential TUs
									for(String component : componentsTU)
									{						
										//get TUs
										if (tuMap.containsKey(component))
										{
											Map<String, Collection<String>> tu = tuMap.get(component);
											// get components of the TUs including genes
											if (tu.containsKey(COMPONENTS))
											{
												regulatedGenes.addAll(tu.get(COMPONENTS)); //potential genes, but not only genes
											}
										}	
									}
								}
							}
							if (tuMap.containsKey(regulated))
							{
								Map<String, Collection<String>> tu = tuMap.get(regulated);
								regulatedTUs.add(regulated); //potential TUs
								// get components of the TUs including genes
								if (tu.containsKey(COMPONENTS))
								{
									regulatedGenes.addAll(tu.get(COMPONENTS)); //potential genes, but not only genes
								}
							}
						}
					}
				}

			}
			regulatedGenes.retainAll(geneMap.keySet());
			regulatedTUs.retainAll(tuMap.keySet());
			Collection<String> genesSet = regulationGenes.get(entry.getKey());
			if (genesSet == null)
			{
				//System.err.println("Error: Key "+ key + " already exists:\n" +
				//		"values in map: " + regulationGenes.get(key) +
				//		"\nnew values:    " + regulatedGenes);
				//System.exit(-1);
				genesSet = new TreeSet<String>();
				regulationGenes.put(entry.getKey(), genesSet);
			}
			genesSet.addAll(regulatedGenes);

			Collection<String> tuSet = regulationTUs.get(entry.getKey());
			if (tuSet == null)
			{
				//System.err.println("Error: Key "+ key + " already exists:\n" +
				//		"values in map: " + regulationGenes.get(key) +
				//		"\nnew values:    " + regulatedGenes);
				//System.exit(-1);
				tuSet = new TreeSet<String>();
				regulationTUs.put(entry.getKey(), tuSet);
			}
			tuSet.addAll(regulatedTUs);
		}

		int output_number = 0;
		BufferedWriter out = new BufferedWriter(new FileWriter(fileOut));
		for (Entry<String, Collection<String>> entry : regulationGenes.entrySet())
		{
			if (! entry.getValue().isEmpty())
			{
				output_number++;
				out.write(entry.getKey() + "\t" + entry.getValue().toString().replaceAll("[\\[\\],]", "")+"\n");
			}
			else
			{
				System.err.println(entry.getKey() + " does not regulate gene(s)");
			}
		}

		System.err.println(output_number + " regulons in output");
		out.close();
	}

	private static Collection<String> getGenes(String proteinCplx, Map<String, Map<String, Collection<String>>> map)
	{
		Collection<String> result = new HashSet<String>();
		if(map.containsKey(proteinCplx))
		{
			Map<String, Collection<String>> p = map.get(proteinCplx);
			if (p.containsKey(GENE))
			{
				result.addAll(p.get(GENE));
			}
			if (p.containsKey(COMPONENTS))
			{
				for (String protein :p.get(COMPONENTS))
				{
					result.addAll(getGenes(protein, map));
				}
			}
		}
		return result;
	}
}
